var babel = require('@babel/core')
var traverse = require('@babel/traverse').default;
// var template = require('@babel/template').default
var t = require('@babel/types')
var generate = require('@babel/generator').default
var code = `
const created = 123
const mounted = 456
export default {
  name: 'app',
  components: {
    HelloWorld:'hello world'
  },
  created(){
    console.log(123);
    function name(params) {
      
    }
    a=1
  },
  created:test(function(){
    console.log('bbb')
  }),
  mounted:test(()=>{
    console.log('mounted');
  }),
  mounted(){
    console.log(321);
  },
  methods: {
    
  },
}`

function isType(params) {
  return Object.prototype.toString.call(params).slice(8,-1)
}


// 插入方法调用表达式
function createdCallExpression(params){
  var dateNow = t.callExpression(
    t.memberExpression(
      t.identifier('Date'),
      t.identifier('now'),
    ),[]
  )
  var perfDomLoading = t.memberExpression(
    t.memberExpression(
      t.memberExpression(t.identifier('window'),t.identifier('performance')),
      t.identifier('timing'),
    ),
    t.identifier('domLoading'),
  )
  var time = t.binaryExpression(
    "-",
    dateNow,
    perfDomLoading
  )
  var msg1 = '当前时间戳：'
  var msg2 = ' ,dom开始解析时间：'
  var template1 = t.templateLiteral([t.templateElement(
    {raw:msg1},
  ),t.templateElement(
    {raw:msg2},
  ),t.templateElement(
    {raw:''},
  )],[dateNow,perfDomLoading])

  var template2 = t.templateLiteral([t.templateElement(
    {raw:'两者之差'},
  ),t.templateElement(
    {raw:'ms'},true
  )],[time])
  params.push(template1)
  params.push(template2)
  var node = t.blockStatement([
    t.expressionStatement(
      t.callExpression(
        t.memberExpression(
          t.identifier('console'),
          t.identifier('log'),
        ),params
      ),
    )
  ])
  return node
}


// 插入方法 created mounted等
function createdMethod(methodName,params) {
  var node = 
    t.objectMethod(
      'method',
      t.identifier(methodName),
      [],
      t.blockStatement([
        createdCallExpression(params)
      ])
    )
  return node
}
// 根据参数类型 生成ast字面量
function generateLiteral(params) {
  // 这里暂时判断五种类型
  // declare type Literal = StringLiteral | NumericLiteral | NullLiteral | BooleanLiteral | RegExpLiteral | TemplateLiteral | BigIntLiteral | DecimalLiteral;
  var literalMap = {
    'String': ()=>t.stringLiteral(params),
    'Number': ()=>t.numericLiteral(params),
    'Null': ()=>t.nullLiteral(),
    'Boolean': ()=>t.booleanLiteral(params),
    'RegExp': ()=>t.regExpLiteral(...arguments)
  }
  var type = isType(params)
  var generateParam = literalMap[type]
  if(!generateParam) {
    throw new Error(`params type expected String|Number|Null|Boolean|RegExp but got ${type}`)
  }
  return generateParam()
}
/**
 * 
 * @param {*} sourceCode 源码
 * @param {*} methodsParams object {key:方法名，value:[String|Number|Null|Boolean|RegExp] 参数}
 */
function transformCode(sourceCode,methodsParams){
  var parseAst = babel.parse(sourceCode)
  // 多个同名方法 只插入一次
  var transformMethods = {}
  Object.keys(methodsParams).forEach(key=>{
    transformMethods[key] = false
  })
  
  traverse(parseAst,{
    Identifier(path){
      const parentPath = path.findParent(path=>t.isExportDefaultDeclaration(path))
      // 只在export default下面path 
      if(!parentPath){
        return
      }
      for(var methodName in methodsParams){
        // 如果存在created mounted等方法 在方法头部插入函数调用
        if(t.isIdentifier(path.node,{name:methodName})){
          if(!transformMethods[methodName]){
            // 生成 表达式->表达式调用->表达式成员->(成员1 成员2), (传参)
            var paramsValue = methodsParams[methodName]
            if(isType(paramsValue)!=='Array'){
              throw new Error(`methodsParams value expected Array but got ${isType(paramsValue)}`)
            }
            // 生成参数字面量
            paramsValue = paramsValue.map(value=>generateLiteral(value))
            var newNode = createdCallExpression(paramsValue)
            // 如果是对象属性 不是函数 类似{created:()=>{}}
            if(!path.parentPath.isMethod()){
              var valueBody = path.parentPath.get('value').get('body')
              if(valueBody.node){
                valueBody.unshiftContainer('body',newNode)
                transformMethods[methodName] = true
              }
            }else{
              // 类似 {created(){}}
              path.parentPath.get('body').unshiftContainer('body',newNode)
              transformMethods[methodName] = true
            }
          }
        }    
      }
    },
  })
  console.log(transformMethods,'transformMethods');
  // 如果源码中没create mounted等方法 就创建并插入到代码中
  Object.keys(transformMethods).forEach(key=>{
    // 没有写created等函数就生成函数
    if(!transformMethods[key]) {
      var paramsValue = methodsParams[key]
      traverse(parseAst,{
        ExportDefaultDeclaration(path){
          paramsValue = paramsValue.map(value=>generateLiteral(value))
          var newNode = createdMethod(key,paramsValue)
          if(newNode){
            path.get('declaration').unshiftContainer('properties', newNode)
          }
        }
      })
    }
  })
  var output = generate(parseAst, {}, sourceCode)
  return output
}

module.exports = transformCode
// var output = transformCode(code,{
//   created:['created','test',false],
//   mounted:['mounted','test',true]
// })
// console.log(output,'code');